/*
 * Created by SharpDevelop.
 * Author: Joey
 * Date: 24/09/2007
 * Time: 23:48
 * 
 * Source is copyright Joey Sabey 2007
 */

using System;
using System.Xml;
using System.Windows.Forms;
using System.Text.RegularExpressions;

namespace MonoTheism
{
	/// <summary>
	/// Description of PRAY_XML.
	/// </summary>
	public class PRAY_XML
	{
		private XmlDocument xmlFile = null;
		public XmlDocument XMLFile
		{
			get{return xmlFile;}
			set{xmlFile = value;}
		}
		public string FileName
		{
			get
			{
				XmlNode xnodeRoot = xmlFile.DocumentElement;
			
				foreach(XmlNode xnodeSecond in xnodeRoot)
				{
					if(xnodeSecond.Name == "filename")
						return xnodeSecond.FirstChild.Value;
				}
				throw new Exception(".pray.xml file does not include " +
				                    "a filename to build to.");
			}
		}
		
		public PRAY_XML(string sFileName)
		{
			xmlFile = new XmlDocument();
			xmlFile.Load(sFileName);
		}
		public PRAY_XML(string sFileName,
		                TreeNodeCollection tnc)
		{
			xmlFile = new XmlDocument();
			Create(tnc, sFileName);
		}
		
		
		
		private void Create(TreeNodeCollection tnc, string s)
		{
			XmlNode xnodeRoot;
			XmlNode xnodeBuffer;
				
			try
            {
            	xmlFile.Load(s);
            }
            catch(System.IO.FileNotFoundException)
            {
            	XmlTextWriter xmlWriter = new XmlTextWriter(s, System.Text.Encoding.UTF8);
            	xmlWriter.Formatting = Formatting.Indented;
            	xmlWriter.WriteProcessingInstruction("xml", "version='1.0' encoding='UTF-8'");
            	xmlWriter.WriteStartElement("pray");
            	xmlWriter.Close();
            	xmlFile.Load(s);
            }
            
            xnodeRoot = xmlFile.DocumentElement;
            xnodeRoot.RemoveAll();
            
            xnodeBuffer = xmlFile.CreateElement("filename");
            XmlNode xtextName = xmlFile.CreateTextNode("CHANGETHIS.agents");
            xnodeBuffer.AppendChild(xtextName);
            xnodeRoot.AppendChild(xnodeBuffer);
            
            xnodeBuffer = xmlFile.CreateElement("meta");
            XmlNode xnodeMetaName = xmlFile.CreateElement("author");
            XmlNode xtextMetaName = xmlFile.CreateTextNode(Options.MetaName);
            xnodeMetaName.AppendChild(xtextMetaName);
            xnodeBuffer.AppendChild(xnodeMetaName);
            xnodeRoot.AppendChild(xnodeBuffer);
            
            xnodeBuffer = xmlFile.CreateElement("blocks");
            int x = 0;
            foreach(TreeNode tn in tnc)
            {
            	XmlNode xnodeSecond = xmlFile.CreateElement("block");
            	XmlAttribute xattrID = xmlFile.CreateAttribute("id");
            	XmlNode xnodeText = xmlFile.CreateTextNode(x.ToString());
            	xattrID.AppendChild(xnodeText);
            	xnodeSecond.Attributes.Append(xattrID);
            	Block tag = (Block)tn.Tag;
            	if(tag.Type() == "FILE")
            	{
            		FILE_Block fbTag = (FILE_Block)tag;
            		
            		XmlNode xnodeType = xmlFile.CreateElement("type");
            		xnodeText = xmlFile.CreateTextNode("FILE");
            		xnodeType.AppendChild(xnodeText);
            		xnodeSecond.AppendChild(xnodeType);
            		
            		XmlNode xnodeName = xmlFile.CreateElement("name");
            		xnodeText = xmlFile.CreateTextNode(fbTag.EmbedName);
            		xnodeName.AppendChild(xnodeText);
            		xnodeSecond.AppendChild(xnodeName);
            		
            		XmlNode xnodePath = xmlFile.CreateElement("path");
            		xnodeText = xmlFile.CreateTextNode(fbTag.Source);
            		xnodePath.AppendChild(xnodeText);
            		xnodeSecond.AppendChild(xnodePath);
            	}
            	if(tag.Type() == "AGNT")
            	{
            		xnodeSecond = CreateAGNTBlock((AGNT_Block)tag,
            		                              xnodeSecond);
            	}
            	x++;
            	xnodeBuffer.AppendChild(xnodeSecond);
            }
            xnodeRoot.AppendChild(xnodeBuffer);
            xmlFile.Save(s);
		}
		
		
		
		private XmlNode CreateAGNTBlock(AGNT_Block abBuffer,
		                                XmlNode xnodeBlock)
		{
			XmlNode xnodeText;
			
			XmlNode xnodeType = xmlFile.CreateElement("type");
           	xnodeText = xmlFile.CreateTextNode("AGNT");
           	xnodeType.AppendChild(xnodeText);
           	xnodeBlock.AppendChild(xnodeType);
           	
           	XmlNode xnodeName = xmlFile.CreateElement("name");
           	xnodeText = xmlFile.CreateTextNode(abBuffer.Name);
           	xnodeName.AppendChild(xnodeText);
           	xnodeBlock.AppendChild(xnodeName);
			
           	xnodeBlock.AppendChild(CreateAGNTData(abBuffer));
           	
           	xnodeBlock.AppendChild(CreateAGNTDependencies(abBuffer));
           	
           	xnodeBlock.AppendChild(CreateAGNTScripts(abBuffer));
           	
			return xnodeBlock;
		}
		
		
		
		private XmlNode CreateAGNTData(AGNT_Block abBuffer)
		{
			XmlNode xnodeText;
			XmlNode xnodeData = xmlFile.CreateElement("data");
			
			XmlNode xnodeBioenergy = xmlFile.CreateElement("bioenergy");
			xnodeText = xmlFile.CreateTextNode(abBuffer.BioenergyValue.ToString());
			xnodeBioenergy.AppendChild(xnodeText);
			xnodeData.AppendChild(xnodeBioenergy);
			
			XmlNode xnodeType = xmlFile.CreateElement("type");
			xnodeText = xmlFile.CreateTextNode(abBuffer.AgentType.ToString());
			xnodeType.AppendChild(xnodeText);
			xnodeData.AppendChild(xnodeType);
			
			XmlNode xnodeAnimFile = xmlFile.CreateElement("anim_file");
			xnodeText = xmlFile.CreateTextNode(abBuffer.AnimFile);
			xnodeAnimFile.AppendChild(xnodeText);
			xnodeData.AppendChild(xnodeAnimFile);
			
			XmlNode xnodeAnimGallery = xmlFile.CreateElement("anim_gallery");
			xnodeText = xmlFile.CreateTextNode(abBuffer.AnimGallery);
			xnodeAnimGallery.AppendChild(xnodeText);
			xnodeData.AppendChild(xnodeAnimGallery);
			
			XmlNode xnodeAnimString = xmlFile.CreateElement("anim_string");
			xnodeText = xmlFile.CreateTextNode(abBuffer.AnimString);
			xnodeAnimString.AppendChild(xnodeText);
			xnodeData.AppendChild(xnodeAnimString);
			
			//TODO: Recognise raw/path!
			XmlNode xnodeRemoveScript = xmlFile.CreateElement("remove_script");
			xnodeText = xmlFile.CreateTextNode(abBuffer.RemoveScript);
			xnodeRemoveScript.AppendChild(xnodeText);
			xnodeData.AppendChild(xnodeRemoveScript);
			
			return xnodeData;
		}
		
		
		
		private XmlNode CreateAGNTDependencies(AGNT_Block abBuffer)
		{
			XmlNode xnodeDependencies = xmlFile.CreateElement("dependencies");
			for(int x=0; x < abBuffer.DependencyCount; x++)
			{
				XmlNode xtextBuffer;
				XmlNode xnodeDependency = xmlFile.CreateElement("dependency");
				
				XmlAttribute xattrNo = xmlFile.CreateAttribute("no");
				xtextBuffer = xmlFile.CreateTextNode((x+1).ToString());
				xattrNo.AppendChild(xtextBuffer);
				xnodeDependency.Attributes.Append(xattrNo);
				
				XmlNode xnodeCategory = xmlFile.CreateElement("category");
				xtextBuffer = xmlFile.CreateTextNode(abBuffer.DependencyCategories[x].ToString());
				xnodeCategory.AppendChild(xtextBuffer);
				xnodeDependency.AppendChild(xnodeCategory);
					
				XmlNode xnodeName = xmlFile.CreateElement("name");
				xtextBuffer = xmlFile.CreateTextNode(abBuffer.Dependencies[x]);
				xnodeName.AppendChild(xtextBuffer);
				xnodeDependency.AppendChild(xnodeName);
				
				xnodeDependencies.AppendChild(xnodeDependency);
			}
			return xnodeDependencies;
		}
		
		
		
		private XmlNode CreateAGNTScripts(AGNT_Block abBuffer)
		{
			XmlNode xnodeScripts = xmlFile.CreateElement("scripts");
			int x;
			for(x=0; x < abBuffer.ScriptCount; x++)
			{
				XmlNode xtextBuffer;
				XmlNode xnodeScript = xmlFile.CreateElement("script");
				
				XmlAttribute xattrNo = xmlFile.CreateAttribute("no");
				xtextBuffer = xmlFile.CreateTextNode((x+1).ToString());
				xattrNo.AppendChild(xtextBuffer);
				xnodeScript.Attributes.Append(xattrNo);
				
				//TODO: Recognise raw/path!
				XmlAttribute xattrType = xmlFile.CreateAttribute("type");
				xtextBuffer = xmlFile.CreateTextNode("path");
				xattrType.AppendChild(xtextBuffer);
				xnodeScript.Attributes.Append(xattrType);
				
				xtextBuffer = xmlFile.CreateTextNode(abBuffer.Scripts[x]);
				xnodeScript.AppendChild(xtextBuffer);
				
				xnodeScripts.AppendChild(xnodeScript);
			}
			
			XmlNode xnodeCount = xmlFile.CreateElement("count");
			XmlNode xtextCount = xmlFile.CreateTextNode(x.ToString());
			xnodeCount.AppendChild(xtextCount);
			xnodeScripts.AppendChild(xnodeCount);
			
			return xnodeScripts;
		}
		
		
		
		public void Save()
		{
			xmlFile.Save(this.FileName);
		}
		
		
		
		public Block_List Load()
		{
			bool bThrow = true;
			Block_List blItems = new Block_List();
			XmlNode xnodeRoot = xmlFile.DocumentElement;
			
			if(xnodeRoot.ChildNodes.Count < 1)
				throw new Exception("File is terminally empty!");
			
			foreach(XmlNode xnodeSecond in xnodeRoot)
			{
				if(xnodeSecond.Name == "blocks")
				{
					blItems = LoadBlocks(xnodeSecond);
					bThrow = false;
					break;
				}
			}
			if(bThrow)
				throw new Exception("No <blocks> tag present.");
			
			return blItems;
		}
		
		
		
		private Block_List LoadBlocks(XmlNode xnodeBlocks)
		{
			Block_List blBuffer = new Block_List();
			foreach(XmlNode xnodeBlock in xnodeBlocks)
			{	
				string type = null;
				foreach(XmlNode xnodeType in xnodeBlock)
				{
					if(xnodeType.Name == "type")
					{
						type = xnodeType.FirstChild.Value;
					}
				}
				if(type == "FILE")
				{
					blBuffer.Add(LoadFILE(xnodeBlock));
				}
				if(type == "AGNT")
				{
					blBuffer.Add(LoadAGNT(xnodeBlock));
				}
			}
			return blBuffer;
		}
		
		
		
		private Block LoadFILE(XmlNode xnodeBlock)
		{
			string name = null;
			string path = null;
			foreach(XmlNode xnodeBuffer in xnodeBlock)
			{
				if(xnodeBuffer.Name == "name")
					name = xnodeBuffer.FirstChild.Value;
				if(xnodeBuffer.Name == "path")
					path = xnodeBuffer.FirstChild.Value;
			}
			FILE_Block blockBuffer = new FILE_Block(path, name);
			return blockBuffer;
		}
		
		
		
		private Block LoadAGNT(XmlNode xnodeBlock)
		{
			AGNT_Block abBuffer = null;
			bool bThrow = true;
			
			foreach(XmlNode xnodeName in xnodeBlock)
			{
				if(xnodeName.Name == "name")
				{
					abBuffer = new AGNT_Block(xnodeName.FirstChild.Value);
					bThrow = false;
					break;
				}
			}
			if(bThrow)
				throw new Exception("AGNT block with no <name> tag in .pray.xml file.");
			bThrow = true;
			
			foreach(XmlNode xnodeData in xnodeBlock)
			{
				if(xnodeData.Name == "data")
				{
					abBuffer = LoadAGNTData(xnodeData, abBuffer);
					bThrow = false;
					break;
				}
			}
			if(bThrow)
				throw new Exception("AGNT block with no <data> tag in .pray.xml file.");
			bThrow = true;
			
			foreach(XmlNode xnodeDependencies in xnodeBlock)
			{
				if(xnodeDependencies.Name == "dependencies")
				{
					abBuffer = LoadAGNTDependencies(xnodeDependencies, abBuffer);
					bThrow = false;
					break;
				}
			}
			if(bThrow)
				throw new Exception("AGNT block with no <dependencies> tag in .pray.xml file.");
			bThrow = true;
			
			foreach(XmlNode xnodeScripts in xnodeBlock)
			{
				if(xnodeScripts.Name == "scripts")
				{
					abBuffer = LoadAGNTScripts(xnodeScripts,
					                           abBuffer);
					bThrow = false;
					break;
				}
			}
			if(bThrow)
				throw new Exception("AGNT block with no <scripts> tag in .pray.xml file.");
			bThrow = true;
			
			return abBuffer;
		}
		
		
		
		private AGNT_Block LoadAGNTData(XmlNode xnodeData,
		                               AGNT_Block abBuffer)
		{
			string sError = null;
			
			sError = "<bionenergy> tag not found";
			foreach(XmlNode xnodeBioenergy in xnodeData)
			{
				if(xnodeBioenergy.Name == "bioenergy")
				{
					abBuffer.BioenergyValue = Int32.Parse(xnodeBioenergy.FirstChild.Value);
					sError = null;
					break;
				}
			}
			if(sError != null)
				throw new Exception(sError);
			
			sError = "<type> tag not found";
			foreach(XmlNode xnodeType in xnodeData)
			{
				if(xnodeType.Name == "type")
				{
					abBuffer.AgentType = Int32.Parse(xnodeType.FirstChild.Value);
					sError = null;
					break;
				}
			}
			if(sError != null)
				throw new Exception(sError);
			
			sError = "<anim_file> tag not found";
			foreach(XmlNode xnodeAnimFile in xnodeData)
			{
				if(xnodeAnimFile.Name == "anim_file")
				{
					abBuffer.AnimFile = xnodeAnimFile.FirstChild.Value;
					sError = null;
					break;
				}
			}
			if(sError != null)
				throw new Exception(sError);
			
			sError = "<anim_gallery> tag not found";
			foreach(XmlNode xnodeAnimGallery in xnodeData)
			{
				if(xnodeAnimGallery.Name == "anim_file")
				{
					abBuffer.AnimGallery = xnodeAnimGallery.FirstChild.Value;
					sError = null;
					break;
				}
			}
			if(sError != null)
				throw new Exception(sError);
			
			sError = "<anim_string> tag not found";
			foreach(XmlNode xnodeAnimString in xnodeData)
			{
				if(xnodeAnimString.Name == "anim_string")
				{
					abBuffer.AnimString = xnodeAnimString.FirstChild.Value;
					sError = null;
					break;
				}
			}
			if(sError != null)
				throw new Exception(sError);
			
			// +-------------------------------------+
			// | !!! Needs to recognise raw/path ||| |
			// +-------------------------------------+
			// TODO: Recognise <remove_script> raw/path
			sError = "<remove_script> tag not found";
			foreach(XmlNode xnodeRemoveScript in xnodeData)
			{
				if(xnodeRemoveScript.Name == "remove_script")
				{
					abBuffer.RemoveScript = xnodeRemoveScript.FirstChild.Value;
					sError = null;
					break;
				}
			}
			if(sError != null)
				throw new Exception(sError);
			
			return abBuffer;
		}
		
		
		
		private AGNT_Block LoadAGNTDependencies(XmlNode xnodeDependencies,
		                                       AGNT_Block abBuffer)
		{
			//string sError = null;
			
			foreach(XmlNode xnodeDependency in xnodeDependencies)
			{
				if(xnodeDependency.Name == "dependency")
				{
					int iCategory = Int32.Parse(LoadAGNTDependencyCategory(xnodeDependency));
					string sName = LoadAGNTDependencyName(xnodeDependency);
					abBuffer.AddDependency(sName, iCategory);
				}
			}
			
			return abBuffer;
		}

		
		
		private string LoadAGNTDependencyCategory(XmlNode xnodeDependency)
		{
			bool bThrow = true;
			string sCategory = null;
			foreach(XmlNode xnodeCategory in xnodeDependency)
			{
				if(xnodeCategory.Name == "category")
				{
					sCategory = xnodeCategory.FirstChild.Value;
					bThrow = false;
					break;
				}
			}
			if(bThrow)
				throw new Exception("Dependency with no category in AGNT block");
			return sCategory;
		}
		
		
		
		private string LoadAGNTDependencyName(XmlNode xnodeDependency)
		{
			string sName = null;
			foreach(XmlNode xnodeName in xnodeDependency)
			{
				if(xnodeName.Name == "name")
				{
					sName = xnodeName.FirstChild.Value;
				}
			}
			if(sName == null)
				throw new Exception("Dependency with no name in AGNT block");
			return sName;
		}
		
		
		
		private AGNT_Block LoadAGNTScripts(XmlNode xnodeScripts,
		                                   AGNT_Block abBuffer)
		{
			foreach(XmlNode xnodeScript in xnodeScripts)
			{
				if(xnodeScript.Name == "script")
				{
					abBuffer.AddScript(xnodeScript.FirstChild.Value);
				}
			}
			return abBuffer;
		}
	}
}
